<template>
  <div class="v-tooltip" v-on="outerEvents" ref="popperContainerNode">
    <div class="v-tooltip__trigger" ref="triggerNode" v-on="events">
      <slot />
    </div>
    <Transition :name="transition">
      <div v-if="isOpen" class="v-tooltip__popper" ref="popperNode">
        <slot name="content"> {{ content }}</slot>
        <div id="arrow" data-popper-arrow></div>
      </div>
    </Transition>
  </div>
</template>

<script lang="ts" setup>
import { ref, watch, reactive, onUnmounted, computed } from 'vue'
import { Instance, createPopper } from '@popperjs/core'
import { debounce } from 'lodash-es'
import type { TooltipProps, TooltipEmits, TooltipInstance } from './types'
import { EventType } from './types'
import useClickOutside from '../../hooks/useClickOutside'

const props = withDefaults(defineProps<TooltipProps>(), {
  placement: 'bottom',
  trigger: 'hover',
  transition: 'fade',
  openDelay: 0,
  closeDelay: 0,
})
const emits = defineEmits<TooltipEmits>()

const isOpen = ref<boolean>(false)
const popperNode = ref<HTMLElement>()
const triggerNode = ref<HTMLElement>()
const popperContainerNode = ref<HTMLElement>()
let popperInstance: Instance | null = null
let events: Record<string, any> = reactive({})
let outerEvents: Record<string, any> = reactive({})
const popperOptions = computed(() => {
  return {
    placement: props.placement,
    modifiers: [
      {
        name: 'offset',
        options: {
          offset: [0, 9],
        },
      },
    ],
    ...props.popperOptions,
  }
})

const togglePopper = () => {
  /* isOpen.value = !isOpen.value
  emits('visible-change', isOpen.value) */
  if (isOpen.value) {
    closeFinal()
  } else {
    // open()
    openFinal()
  }
}

const open = () => {
  // setTimeout(() => {
  isOpen.value = true
  emits('visible-change', isOpen.value)
  // }, props.openDelay)
}
const openDebounce = debounce(open, props.openDelay)
const openFinal = () => {
  closeDebounce.cancel()
  openDebounce()
}

const close = () => {
  // setTimeout(() => {
  isOpen.value = false
  emits('visible-change', isOpen.value)
  // }, props.closeDelay)
}
const closeDebounce = debounce(close, props.closeDelay)
const closeFinal = () => {
  openDebounce.cancel()
  closeDebounce()
}

useClickOutside(popperContainerNode, () => {
  if (props.trigger === 'click' && isOpen.value && !props.manual) {
    closeFinal()
  }
  if (isOpen.value) {
    emits('click-outside', true)
  }
})

const attachEvents = () => {
  if (props.trigger === EventType.hover) {
    events['mouseenter'] = openFinal
    outerEvents['mouseleave'] = closeFinal
  } else if (props.trigger === EventType.click) {
    events['click'] = togglePopper
  }
}

if (!props.manual) {
  attachEvents()
}

watch(
  () => props.manual,
  (isManual) => {
    if (isManual) {
      events = {}
      outerEvents = {}
    } else {
      attachEvents()
    }
  }
)
watch(
  () => props.trigger,
  (newTrigger, oldTrigger) => {
    if (newTrigger !== oldTrigger) {
      // clear the events
      events = {}
      outerEvents = {}
      attachEvents()
    }
  }
)
watch(
  isOpen,
  (newVal) => {
    if (newVal) {
      if (popperNode.value && triggerNode.value) {
        popperInstance = createPopper(
          triggerNode.value,
          popperNode.value,
          popperOptions.value
        )
      } else {
        popperInstance?.destroy()
      }
    }
  },
  { flush: 'post' }
)

onUnmounted(() => {
  popperInstance?.destroy()
})

defineOptions({
  name: 'VTooltip',
})
defineExpose<TooltipInstance>({
  show: openFinal,
  hide: closeFinal,
})
</script>
<style lang="scss" scoped></style>
